package myLearnjdk.jdk.net.ssl;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.util.Arrays;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;

/**
 * 创建一个安全的服务端socket必须要经过特定的一些步骤
 * 本类用于演示创建一个安全服务端socket的过程
 * 
 * 1. 通过keytool生成公开密钥和证书
 * 2. 花钱请可信任的第三方认证你的证书
 * 3. 为你使用的算法创建一个SSLContext
 * 4. 为你要使用的证书源创建一个TrustManagerFactory
 * 5. 为你要使用的密钥类型创建一个KeyManagerFactory
 * 6. 为密钥和证书数据库创建一个KeyStore对象，Oracle默认为KeyStroe为JKS
 * 7. 用密钥和证书填充KeyStore对象，例如使用加密所用的口令短语从文件系统中加载
 * 8. 用KeyStore及其口令短语初始化KeyManagerFactory
 * 9. 用KeyManagerFactory中的密钥管理器（必要）、TrustManagerFactory中的信任管理器和一个随机源
 *    来初始化上下文（后面两个参数可以为null）
 * 
 * @author yangcheng  
 * @date 2020年8月13日  
 * @version V1.0
 */
public class SecureOrderTaker {
	public final static int PORT = 7000;
	  public final static String algorithm = "SSL";

	  public static void main(String[] args) {
		try {
			
		  SSLContext context = SSLContext.getInstance(algorithm);
		  
		  // The reference implementation only supports X.509 keys
		  KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
		  
		  // Oracle's default kind of key store
		  KeyStore ks = KeyStore.getInstance("JKS");
		
		  // For security, every key store is encrypted with a 
		  // passphrase that must be provided before we can load 
		  // it from disk. The passphrase is stored as a char[] array
		  // so it can be wiped from memory quickly rather than
		  // waiting for a garbage collector. 
		  char[] password = System.console().readPassword();
		  ks.load(new FileInputStream("jnp4e.keys"), password);
		  kmf.init(ks, password);
		  context.init(kmf.getKeyManagers(), null, null);
		
		  // wipe the password
		  Arrays.fill(password, '0');
		  
		  /**
		   * 通过context来获取SSLServerSocketFactory，并通过此工厂类来
		   * 获取SSLServerSocket
		   */
		  SSLServerSocketFactory factory 
		      = context.getServerSocketFactory();
		 
		  SSLServerSocket server 
		      = (SSLServerSocket) factory.createServerSocket(PORT);
		 
		  /**
		   * 获取支持的所有密码组
		   * add anonymous (non-authenticated) cipher suites
		   * 添加匿名（非身份验证）密码套件
		   */
		  String[] supported = server.getSupportedCipherSuites();
		  String[] anonCipherSuitesSupported = new String[supported.length];      
		  int numAnonCipherSuitesSupported = 0;
		  for (int i = 0; i < supported.length; i++) {
			/**
		 * 获取所有可以匿名的密码套件名称--jdk默认提供了一些可以匿名访问的密码套件
		 */
			  if (supported[i].indexOf("_anon_") > 0) {
				  anonCipherSuitesSupported[numAnonCipherSuitesSupported++] = 
		                                                    supported[i];
			  }
		  }  
		  /**
		   * 获取所有启用的密码组
		   */
		  String[] oldEnabled = server.getEnabledCipherSuites();
		  /**
		   * 创建一个新的密码套件数组，将原有和新建可匿名加密套件整合起来，并将其设置到serverSocket中
		   */
		  String[] newEnabled = new String[oldEnabled.length 
		      + numAnonCipherSuitesSupported];
		  System.arraycopy(oldEnabled, 0, newEnabled, 0, oldEnabled.length);
		  System.arraycopy(anonCipherSuitesSupported, 0, newEnabled, 
		      oldEnabled.length, numAnonCipherSuitesSupported);
		  
		  server.setEnabledCipherSuites(newEnabled);
		 
		  // Now all the set up is complete and we can focus 
		  // on the actual communication. 
		  while (true) {
		    // This socket will be secure,
		// but there's no indication of that in the code!
		        try (Socket theConnection = server.accept()) {
		          InputStream in = theConnection.getInputStream();
		          int c;
		          while ((c = in.read()) != -1) {
		            System.out.write(c);
		          } 
		        } catch (IOException ex) {
		          ex.printStackTrace();
		        } 
		  } 
		  } catch (IOException | KeyManagementException
		       | KeyStoreException | NoSuchAlgorithmException
		       | UnrecoverableKeyException ex) {
			   	ex.printStackTrace();
		  } catch (java.security.cert.CertificateException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
		  }
	  } 
}
